package net.yussi.component.chart.pie3D
{
    import flash.display.BlendMode;
    import flash.display.Graphics;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.filters.DropShadowFilter;
    import flash.geom.Point;
    
    import mx.core.UIComponent;

    public class PieDrawer extends UIComponent
    {
        private var drawAreaUnits:Object;
        private var pieUnitArea:UIComponent;
        private var labelArea:UIComponent;
        private var lineArea:UIComponent;
        private var scaleNum:Number;
        private var lineRadiusScale:Number;
        private var lineGap:Number;
        private var rotationAngle:Number;
        private var nowAngle:Number = 0;
        private var toAngle:Number = 0;
        private var keepData:Array;
        private var openGap:Number = 40;

        public function PieDrawer()
        {
            super();
        }

        private function pieRotateHandler(event:Event):void
        {
            switch (event.type)
            {
                case "PieRotateLeft":
                    toAngle -= 30;
                    break;
                case "PieRotateRight":
                    toAngle += 90;
                    break;
            }

            this.removeEventListener("enterFrame", enterFunc, false);
            this.addEventListener("enterFrame", enterFunc, false, 0, true);
        }

        private function enterFunc(event:Event):void
        {
            var checkMove:Boolean = false;
            var plusAngle:Number = 0;

            if (nowAngle != toAngle)
            {
                checkMove = true;
                plusAngle = (toAngle - nowAngle) * 1;

                if (Math.abs(toAngle - (nowAngle + plusAngle)) < 1)
                {
                    plusAngle = toAngle - nowAngle;
                    nowAngle = toAngle;
                }
                else
                {
                    nowAngle += plusAngle;
                }
            }

            for (var i:int = 0; i < keepData.length; i++)
            {
                var now:Number = keepData[i].fromCenter;
                var toN:Number = keepData[i].toFromCenter;

                keepData[i].startAngle += plusAngle;
                keepData[i].endAngle += plusAngle;

                while (keepData[i].startAngle < 0)
                {
                    keepData[i].startAngle += 360;
                }

                while (keepData[i].endAngle < 0)
                {
                    keepData[i].endAngle += 360;
                }

                if (now == toN)
                {
                    continue;
                }

                checkMove = true;

                if (Math.abs(now - toN) < 1)
                {
                    keepData[i].fromCenter = toN;
                }
                else
                {
                    keepData[i].fromCenter = (toN - now) * 0.3 + now;
                }
            }

            if (checkMove)
            {
                setData(keepData, rotationAngle, lineRadiusScale, lineGap);
            }
            else
            {
                this.removeEventListener("enterFrame", enterFunc, false);
            }
        }
		
		/**
		 * pieUnit을 클릭하면 해당 pieUnit가 이동함 
		 * @param event
		 * 
		 */		
        private function pieUnitAreaClickHandler(event:MouseEvent):void
        {
            //var target:PieUnit = event.target as PieUnit;
            var objData:Object = event.target.objData;
            if (objData.toFromCenter == openGap)
            {
                objData.toFromCenter = 0;
            }
            else
            {
                objData.toFromCenter = openGap;
            }

            this.removeEventListener("enterFrame", enterFunc, false);
            this.addEventListener("enterFrame", enterFunc, false, 0, true);
        }


        public function setData(v:Array, rotationAngle:Number=0, lineRadiusScale:Number=1.4, lineGap:Number=10):void
        {
            this.scaleNum = 0.8;
            this.lineRadiusScale = lineRadiusScale;
            this.lineGap = lineGap;
            this.rotationAngle = rotationAngle;
            this.keepData = v;

            drawAreaUnits = new Object();
            drawAreaUnits.area1 = new Array();
            drawAreaUnits.area2 = new Array();
            drawAreaUnits.area3 = new Array();
            drawAreaUnits.area4 = new Array();

            for (var i:int = 0; i < v.length; i++)
            {
                var obj:Object = v[i];
                obj.startAngle += rotationAngle;
                obj.endAngle += rotationAngle;
                var makedUnit:Array = makePieUnit(obj, obj.startAngle, obj.endAngle, obj.xRadius, obj.yRadius, obj.depth, obj.colorTop, obj.colorSide1, obj.colorSide2, obj.colorSide3, obj.fromCenter);

                for (var j:int = 0; j < makedUnit.length; j++)
                {
                    if (drawAreaUnits["area" + checkArea(makedUnit[j].startAngle)] == null)
                    {
                        drawAreaUnits["area" + checkArea(makedUnit[j].startAngle)] = new Array();
                    }
                    drawAreaUnits["area" + checkArea(makedUnit[j].startAngle)].push(makedUnit[j]);
                }
            }
            drawPie();
            drawLabel(v);
        }

        private function drawLabel(v:Array):void
        {
            if (labelArea == null)
            {
                labelArea = new UIComponent();
                this.addChild(labelArea);
            }
            else
            {
                while (labelArea.numChildren > 0)
                {
                    labelArea.removeChildAt(0);
                }
            }

            if (lineArea == null)
            {
                lineArea = new UIComponent();
                this.addChild(lineArea);
                lineArea.blendMode = BlendMode.INVERT; //?
            }
            else
            {
                while (lineArea.numChildren > 0)
                {
                    lineArea.removeChildAt(0);
                }
            }

            var labelArray1:Array = new Array(); //왼쪽라벨모음
            var labelArray2:Array = new Array(); //오늘쪽라벨모음

            for (var i:int = 0; i < v.length; i++)
            {
                var drawUnit:Object = v[i];
                var centerAngle:Number = ((drawUnit.endAngle - drawUnit.startAngle)) / 2 + drawUnit.startAngle;

                /**
                 * 라벨의 위치와 데이터
                 */
                //var pt:Point = rotationLocate(centerAngle, 0, -(drawUnit.xRadius + lineGap + drawUnit.fromCenter));
                //var pt:Point = wedgePoint(centerAngle, centerAngle, -(drawUnit.xRadius + lineGap + drawUnit.fromCenter), -(drawUnit.yRadius + lineGap + drawUnit.fromCenter));
				var pt:Point = wedgePoint(centerAngle, centerAngle, drawUnit.xRadius, drawUnit.yRadius);
				var labelData:Object = new Object();
                labelData.subject = Math.round((drawUnit.endAngle - drawUnit.startAngle) / 360 * 1000) / 10 + "% " + drawUnit.subject;
                labelData.objData = drawUnit;
                labelData.radius = drawUnit.xRadius + lineGap + drawUnit.fromCenter;
                labelData.xRadius = drawUnit.xRadius;
                labelData.yRadius = drawUnit.yRadius;
                labelData.centerAngle = centerAngle;

                centerAngle = centerAngle % 360;
                if (centerAngle > 180)
                {
					/* 왼쪽 */
					labelData.x = -drawUnit.xRadius;
					labelData.y = pt.y;
					
                    labelArray1.push(labelData);
                }
                else
                {
					/* 왼쪽 */
					labelData.x = drawUnit.xRadius;
					labelData.y = pt.y;
					
                    labelArray2.push(labelData);
                }
            }
			
			/* 라벨의 좌표 배열을  y속성을 기분으로 정렬 */
            labelArray1.sortOn("y", Array.NUMERIC);
            labelArray2.sortOn("y", Array.NUMERIC);

            var keepData:Object;
			/* 라벨의 위치가 겹치지 않도록 y좌표를 수정함 */
			/* 왼쪽 */
            if (labelArray1.length > 1)
            {
                keepData = labelArray1[0];
                for (i = 1; i < labelArray1.length; i++)
                {
                    labelData = labelArray1[i];

                    if (labelData.y < keepData.y + 15)
                    {
                        labelData.y += ((keepData.y + 15) - labelData.y);
                    }

                    if (!(labelData.centerAngle % 360 > 90 && labelData.centerAngle % 360 < 270))
                    {
                        //labelData.x = -calculateSqrt(labelData.radius, labelData.y);
                        var tempAngle:Number = findAngle(labelData.x, labelData.y);
                        pt = rotationLocate(tempAngle, 0, -labelData.radius);
						
                        if (labelData.y <= pt.y)
                        {
                            //labelData.x = pt.x;
                            labelData.y = pt.y;
                        }
                    }
                    keepData = labelData;
                }
            }
			
			/* 오른쪽 */
            if (labelArray2.length > 1)
            {
                keepData = labelArray2[0];
                for (i = 1; i < labelArray2.length; i++)
                {
                    labelData = labelArray2[i];

                    if (labelData.y < keepData.y + 15)
                    {
                        labelData.y += ((keepData.y + 15) - labelData.y);
                    }

                    if (!(labelData.centerAngle % 360 > 90 && labelData.centerAngle % 360 < 270))
                    {
                        //labelData.x = calculateSqrt(labelData.radius, labelData.y);
                        tempAngle = findAngle(labelData.x, labelData.y);
                        pt = rotationLocate(tempAngle, 0, -labelData.radius);

                        if (labelData.y <= pt.y)
                        {
                            //labelData.x = pt.x;
                            labelData.y = pt.y;
                        }
                    }
                    keepData = labelData;
                }
            }

            var g:Graphics = lineArea.graphics;
            g.clear();
            g.lineStyle(1, 0x999999, 1);

            var pt1:Point;
            var pt2:Point;
			
			var centerPoint:Point;
			var centerMovePoint:Point;
			var labelPoint:Point;

            var labelItem:PieLabel;
			
			/* 왼쪽 라벨 */
            for (i = 0; i < labelArray1.length; i++)
            {
				
                labelData = labelArray1[i];
                labelItem = new PieLabel();
                //labelItem.addEventListener("click", pieUnitAreaClickHandler, false, 0, true);
                labelItem.data = labelData.objData;
                labelItem.x = labelData.x - 10;
                labelItem.y = labelData.y;
                labelItem.setText(labelData.subject, "right");
                labelArea.addChild(labelItem);
				
                /*pt1 = wedgePoint(labelData.centerAngle, labelData.centerAngle, labelData.xRadius / 2, labelData.yRadius / 2);
                pt2 = new Point(labelItem.x, labelItem.y);
                gl.moveTo(pt1.x, pt1.y);
                gl.lineTo(pt2.x, pt2.y);*/
                    //gl.lineTo(pt2.x - +labelItem.textWidth - 10, pt2.y);
				
				////////////////////
				centerPoint = wedgePoint(labelData.centerAngle, labelData.centerAngle, labelData.xRadius / 2, labelData.yRadius / 2);
				centerMovePoint = wedgePoint(labelData.centerAngle, labelData.centerAngle, labelData.xRadius, labelData.yRadius);
				labelPoint = new Point(labelItem.x, labelItem.y);
				//중앙으로 이동
				g.moveTo(centerPoint.x, centerPoint.y);
				//중앙에서 반지름 만큼 선긋기
				g.lineTo(centerMovePoint.x, labelPoint.y);
				//라벨까지의 선긋기
				g.lineTo(labelPoint.x, labelPoint.y)
            }

			/* 오른쪽 라벨 */
            for (i = 0; i < labelArray2.length; i++)
            {
                labelData = labelArray2[i];
                labelItem = new PieLabel();
                //labelItem.addEventListener("click", pieUnitAreaClickHandler, false, 0, true);
                labelItem.data = labelData.objData;
                labelItem.x = labelData.x + 10;
                labelItem.y = labelData.y;
                labelItem.setText(labelData.subject, "left");
                labelArea.addChild(labelItem);

                //pt1 = wedgePoint(labelData.centerAngle, labelData.centerAngle, labelData.xRadius / 2, labelData.yRadius / 2);
                //pt2 = new Point(labelItem.x, labelItem.y);
                //gl.moveTo(pt1.x, pt1.y);
                //gl.lineTo(pt2.x, pt2.y);
				//gl.lineTo(pt2.x + labelItem.textWidth + 10, pt2.y);
				
				/////////////////////////////////////
				centerPoint = wedgePoint(labelData.centerAngle, labelData.centerAngle, labelData.xRadius / 2, labelData.yRadius / 2);
				centerMovePoint = wedgePoint(labelData.centerAngle, labelData.centerAngle, labelData.xRadius, labelData.yRadius);
				labelPoint = new Point(labelItem.x, labelItem.y);
				//중앙으로 이동
				g.moveTo(centerPoint.x, centerPoint.y);
				//중앙에서 반지름 만큼 선긋기
				g.lineTo(centerMovePoint.x, labelPoint.y);
				//라벨까지의 선긋기
				g.lineTo(labelPoint.x, labelPoint.y)
            }
        }

        private function calculateSqrt(c:Number, a:Number):Number
        {
            var ret:Number;
            if (c < a)
            {
                return 0;
            }
            ret = Math.sqrt(c * c - a * a);

            return ret;
        }

        private function findAngle(x1:Number, y1:Number, x2:Number=0, y2:Number=0):Number
        {
            var a:Number = y2 - y1;
            var c:Number = Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));

            var ang:Number = Math.acos(a / c);
            ang *= 180.0 / Math.PI;

            if (x1 < 0)
            {
                ang = 360 - ang;
            }
            return ang;
        }

        private function drawPie():void
        {
            if (pieUnitArea == null)
            {
                pieUnitArea = new UIComponent();
                pieUnitArea.scaleY = scaleNum;
                pieUnitArea.addEventListener("click", pieUnitAreaClickHandler, false, 0, true);

                var dropShadow:DropShadowFilter = new DropShadowFilter(20, 90, 0x000000, 0.5, 20, 20, 1, 2, false, false, false);
                pieUnitArea.filters = new Array(dropShadow);

                this.addChild(pieUnitArea);
            }
            else
            {
                while (pieUnitArea.numChildren > 0)
                {
                    pieUnitArea.removeChildAt(0);
                }
            }

            var drawOrder:Array = ["area1","area4","area3","area2"];
            for (var i:int = 0; i < drawOrder.length; i++)
            {
                var drawArray:Array = drawAreaUnits[drawOrder[i]];
                for (var k:int = 0; k < drawArray.length; k++)
                {
                    drawArray[k].startAngle = drawArray[k].startAngle % 360;
                    drawArray[k].endAngle = drawArray[k].endAngle % 360;
                }

                // 정렬
                switch (drawOrder[i])
                {
                    case "area4":
                    case "area3":
                        drawArray.sortOn("startAngle", Array.DESCENDING | Array.NUMERIC);
                        break;
                    default:
                        drawArray.sortOn("startAngle", Array.NUMERIC);
                        break;
                }

                for (var j:int = 0; j < drawArray.length; j++)
                {
                    var drawUnit:Object = drawArray[j];
                    var temp:PieUnit = new PieUnit();
                    temp.x = drawUnit.tx;
                    temp.y = drawUnit.ty;
                    pieUnitArea.addChild(temp);
                    temp.drawPie(drawUnit.objData, drawUnit.startAngle, drawUnit.endAngle, drawUnit.xRadius, drawUnit.yRadius, drawUnit.depth, drawUnit.colorTop, drawUnit.colorSide1, drawUnit.colorSide2, drawUnit.colorSide3);
                }
            }
        }

        /**
         *  drawUnit function
         *  한 개의 파이 조각을 만든다.
         *  파이가 그려질 360도의 범위를 90도 단위로 4조각으로 나누어
         *  각 단위를 넘어설 경우 조각을 나누어 배열로 반환한다.
         *
         */
        private function makePieUnit(objData:Object, 
            startAngle:Number, 
            endAngle:Number, 
            xRadius:Number, 
            yRadius:Number,  
            depth:Number, 
            colorTop:Array, 
            colorSide1:Array, 
            colorSide2:Array, 
            colorSide3:Array, 
            fromCenter:Number = 0):Array
        {
			
            var ret:Array = new Array();
            var retObj:Object;

            var keepAngle:Number = -1;
            var keepStartAngle:Number = startAngle;

            var tx:Number = 0;
            var ty:Number = 0;

            if (fromCenter > 0)
            {
                var toPt:Point = rotationLocate(startAngle + (endAngle - startAngle) / 2, 0, -fromCenter);
                tx = toPt.x;
                ty = toPt.y;
            }

            while (keepAngle != endAngle)
            {
                keepAngle = (Math.floor(keepStartAngle / 90) + 1) * 90;

                if (keepAngle > endAngle)
                {
                    keepAngle = endAngle;
                }
                retObj = new Object();
                retObj.objData = objData;

                retObj.startAngle = (int(keepStartAngle * 10) / 10) % 360;
                retObj.endAngle = (int(keepAngle * 10) / 10) % 360;

                if (retObj.startAngle != retObj.endAngle)
                {
                    retObj.xRadius = xRadius;
                    retObj.yRadius = yRadius;
                    retObj.depth = depth;
                    retObj.colorTop = colorTop;
                    retObj.colorSide1 = colorSide1;
                    retObj.colorSide2 = colorSide2;
                    retObj.colorSide3 = colorSide3;
                    retObj.tx = tx;
                    retObj.ty = ty;
                    ret.push(retObj);
                }
                keepStartAngle = keepAngle;
            }
            return ret;
        }

        /**
         * 90도 단위의 Area중 어느곳에 속하는 지를 반환한다.
         *
         */
        private function checkArea(Angle:Number):int
        {
            return int(Math.floor(Angle % 360 / 90)) + 1;
        }

        private function rotationLocate(nAngle:Number, x1:Number, y1:Number, x0:Number=0, y0:Number=0):Point
        {
            //x0, y0 : 원점
            //x1, y1 : 이동할 점
            var rad:Number = nAngle / 180 * Math.PI;

            var ret:Point = new Point(0, 0);
            ret.x = Math.cos(rad) * (x1 - x0) - Math.sin(rad) * (y1 - y0) + x0;
            ret.y = Math.sin(rad) * (x1 - x0) + Math.cos(rad) * (y1 - y0) + y0;
            return ret;
        }


        /**
         *
         * @param startAngle 시작각
         * @param moveAngle 움직일각
         * @param xRadius X축 반지름
         * @param yRadius Y축 반지름
         * @return
         *
         */
        public function wedgePoint(startAngle:Number, moveAngle:Number, xRadius:Number, yRadius:Number):Point
        {
            if (yRadius >= xRadius)
                yRadius = xRadius;

            var theta:Number = moveAngle * Math.PI / 180;
            var newX:Number = yRadius * Math.cos(theta);
            var newY:Number = xRadius * Math.sin(theta);

            var tempX:Number = newX;
            var tempY:Number = newY;
            if (startAngle >= 0 && startAngle < 90)
            {
                newX = Math.abs(tempY);
                newY = -Math.abs(tempX);
            }
            else if (startAngle >= 90 && startAngle < 180)
            {
                newX = Math.abs(tempY);
                newY = Math.abs(tempX);
            }
            else if (startAngle >= 180 && startAngle < 270)
            {
                newX = -Math.abs(tempY);
                newY = Math.abs(tempX);
            }
            else if (startAngle >= 270 && startAngle < 360)
            {
                newX = -Math.abs(tempY);
                newY = -Math.abs(tempX);
            }

            var point:Point = new Point(newX, newY);
            return point;
        }
    }
}